import os
import shutil
import hashlib
import logging
from pathlib import Path
from datetime import datetime

def setup_logging(log_file):
    """配置日志记录，同时输出到控制台和文件
    
    参数:
        log_file: 日志文件路径
    """
    log_dir = os.path.dirname(log_file)
    os.makedirs(log_dir, exist_ok=True)
    
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    
    # 控制台输出
    ch = logging.StreamHandler()
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    
    # 文件输出
    fh = logging.FileHandler(log_file, encoding='utf-8')
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    
    return logger

def calculate_file_hash(file_path, chunk_size=8192):
    """计算文件的SHA-256哈希值
    
    参数:
        file_path: 文件路径
        chunk_size: 每次读取的字节数，默认8192字节
    
    返回:
        文件哈希值的十六进制字符串，失败时返回None
    """
    hasher = hashlib.sha256()
    try:
        with open(file_path, 'rb') as f:
            while chunk := f.read(chunk_size):
                hasher.update(chunk)
        return hasher.hexdigest()
    except Exception as e:
        logging.error(f"计算文件哈希失败: {file_path} - {str(e)}")
        return None

def copy_files_without_duplicates(source_dir, target_dir, log_file):
    """递归复制目录，跳过重复内容的文件
    
    参数:
        source_dir: 源目录路径
        target_dir: 目标目录路径
        log_file: 日志文件路径
    
    返回:
        操作成功返回True，失败返回False
    """
    if not os.path.exists(source_dir):
        logging.error(f"源目录不存在: {source_dir}")
        return False
    
    # 创建目标目录
    os.makedirs(target_dir, exist_ok=True)
    
    # 记录已复制文件的哈希值
    hashes = set()
    # 记录统计信息
    stats = {
        'total_files': 0,
        'copied': 0,
        'skipped_duplicate': 0,
        'errors': 0
    }
    
    logging.info(f"开始处理源目录: {source_dir}")
    logging.info(f"目标目录: {target_dir}")
    logging.info(f"日志文件: {log_file}")
    
    for root, dirs, files in os.walk(source_dir):
        # 计算相对路径，用于创建目标目录结构
        relative_path = os.path.relpath(root, source_dir)
        if relative_path == '.':
            relative_path = ''
        
        # 创建对应的目标子目录
        target_subdir = os.path.join(target_dir, relative_path)
        try:
            os.makedirs(target_subdir, exist_ok=True)
        except Exception as e:
            logging.error(f"创建目录失败: {target_subdir} - {str(e)}")
            stats['errors'] += 1
            continue
        
        # 处理文件
        for file in files:
            stats['total_files'] += 1
            source_file = os.path.join(root, file)
            target_file = os.path.join(target_subdir, file)
            
            # 计算文件哈希
            file_hash = calculate_file_hash(source_file)
            if file_hash is None:
                stats['errors'] += 1
                continue
            
            # 检查重复
            if file_hash in hashes:
                logging.info(f"跳过重复文件: {source_file}")
                stats['skipped_duplicate'] += 1
                continue
            
            # 复制文件 (保留元数据)
            try:
                shutil.copy2(source_file, target_file)
                hashes.add(file_hash)
                stats['copied'] += 1
                logging.info(f"已复制: {source_file} -> {target_file}")
            except Exception as e:
                logging.error(f"复制文件失败: {source_file} -> {target_file} - {str(e)}")
                stats['errors'] += 1
    
    # 输出统计信息
    logging.info("=" * 50)
    logging.info("处理完成! 统计信息:")
    logging.info(f"总文件数: {stats['total_files']}")
    logging.info(f"已复制: {stats['copied']}")
    logging.info(f"跳过重复: {stats['skipped_duplicate']}")
    logging.info(f"错误数: {stats['errors']}")
    
    return True

if __name__ == "__main__":
    """
    目录文件复制去重工具
    
    功能:
    1. 递归搜索指定目录及其子目录
    2. 将文件复制到指定目标目录
    3. 通过哈希值检测并跳过重复文件
    4. 记录详细操作日志到控制台和文件
    
    使用方法:
    1. 运行程序后，按提示输入源目录路径和目标目录路径
    2. 程序会自动在当前目录生成带时间戳的日志文件
    3. 查看控制台输出或日志文件了解详细操作过程
    
    注意事项:
    - 确保有足够权限读取源目录和写入目标目录
    - 处理大量文件或大文件时可能需要较长时间
    - 哈希计算采用分块读取，避免内存溢出
    """
    print("=" * 50)
    print("  目录文件复制去重工具 ")
    print("=" * 50)
    
    # 配置参数
    SOURCE_DIR = input("请输入源目录路径: ").strip()
    TARGET_DIR = input("请输入目标目录路径: ").strip()
    
    # 自动生成日志文件路径
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    LOG_FILE = os.path.join(os.getcwd(), f"copy_operation_{timestamp}.log")
    
    print("\n配置信息:")
    print(f"源目录: {SOURCE_DIR}")
    print(f"目标目录: {TARGET_DIR}")
    print(f"日志文件: {LOG_FILE}")
    print("=" * 50)
    
    # 执行复制操作
    setup_logging(LOG_FILE)
    copy_files_without_duplicates(SOURCE_DIR, TARGET_DIR, LOG_FILE)